#ifndef GEOMETRY_H
#define GEOMETRY_H

#include "mathengine.h"
#include "color.h"

namespace raster {

struct Vertex {
    mathengine::Vec4f pos; // position
    mathengine::Vec4f normal;
    Color color;

    Vertex() = default;
    Vertex(std::initializer_list<float> poslist) {
        int i = 0;
        for (auto p : poslist) {
            pos[i++] = p;
        }
    }
    Vertex(const Vertex& v) : pos(v.pos), normal(v.normal), color(v.color) {
    }
    Vertex(const mathengine::Vec4f& pos) : pos(pos), normal(), color() {
    }
    Vertex(const mathengine::Vec4f& pos, const mathengine::Vec4f& normal) : 
        pos(pos), normal(normal), color() {
    }
    Vertex(const mathengine::Vec4f& pos, const mathengine::Vec4f& normal, const Color& color):
        pos(pos), normal(normal), color(color) {}

    Vertex& operator=(const Vertex& v) {
        if (this != &v) {
            pos = v.pos;
            normal = v.normal;
            color = v.color;
        }
        return *this;
    }

    friend std::ostream& operator<<(std::ostream& os, const Vertex& v) {
        return os << "pos = " << v.pos << ", normal = " << v.normal << ", color = " << v.color;
    }
};

/// Vertex linear interpolation
/// (1 - t) * v1 + t * v2
inline Vertex Lerp(const Vertex& v1, const Vertex& v2, float t) {
    Vertex res;
    res.pos = Lerp(v1.pos, v2.pos, t);
    res.color = ColorLerp(v1.color, v2.color, t);
    res.normal = Lerp(v1.normal, v2.normal, t);
    return res;
}

struct Line {
    Vertex v0, v1; // 2 vertex of a line

    Line() {}
    Line(const Vertex& v0, const Vertex& v1, const bool isNull = false) : v0(v0), v1(v1), isNull(isNull) {}

    void setVertexPos(int i, const mathengine::Vec4f& pos) {
        (*this)[i].pos = pos;
    }
    void setVertexNormal(int i, const mathengine::Vec4f& n) {
        (*this)[i].normal = n;
    }
    void setVertexColor(int i, const Color& c) {
        (*this)[i].color = c;
    }

    Vertex& operator[](int i) {
        if (i == 0) return v0;
        else return v1;
    }

    bool empty() const { return isNull; }

    friend std::ostream& operator<<(std::ostream& os, const Line& v) {
        return os << "Line: v0(" << v.v0 << "), v1(" << v.v1 << ")";
    }

private:
    bool isNull = true;

};

struct Triangle {
    Vertex v[3]; // 3 vertex of a triangle

    Triangle() { }
    Triangle(const Vertex vv[3]) : v{vv[0], vv[1], vv[2]} { };
    Triangle(const Vertex& v0, const Vertex& v1, const Vertex& v2) : v{v0, v1, v2} { };
    Triangle(mathengine::Vec3f vv[3]) {
        v[0].pos = mathengine::Vec4f({vv[0].x(), vv[0].y(), vv[0].z(), 1});
        v[1].pos = mathengine::Vec4f({vv[1].x(), vv[1].y(), vv[1].z(), 1});
        v[2].pos = mathengine::Vec4f({vv[2].x(), vv[2].y(), vv[2].z(), 1});
    }

    void setVertexPos(int i, const mathengine::Vec4f& pos) {
        v[i].pos = pos;
    }
    void setVertexNormal(int i, const mathengine::Vec4f& n) {
        v[i].normal = n;
    }
    void setVertexColor(int i, const Color& c) {
        v[i].color = c;
    }

    Vertex& operator[](int i) {
        return v[i];
    }

    friend std::ostream& operator<<(std::ostream& os, const Triangle& v) {
        return os << "Triangle:\nv0(" << v.v[0] << "),\nv1(" << v.v[1] << "),\nv2(" << v.v[2] << ")";
    }
};

}

#endif // GEOMETRY_H
